function convert(input) { var rootNode = $(input).contents()[0].parentNode; var childrenText = traverse(input, 0); var att = AttributesText(rootNode, false); var delimeter = (childrenText != '' && att != '' ? ', ' : ''); return beautify('{"' + rootNode.nodeName.toLowerCase() + '": ' + childrenText + delimeter + att + '}'); } function traverse(tree, depth) { var result = ''; var nodes = $(tree).contents().filter(function (index) { // exclude all carriage returns return (this.nodeType != 3 || $(this).text().charCodeAt(0) != 10); }) var i = 0; var wrapWithBraces = false; var wrapWithSquareBrackets = false; var lastNodeName = ''; nodes.each(function (nodeIndex) { if (this.nodeType == 3) { // text node var value = $(this).text(); if (value.charCodeAt(0) == 10) { //result += '\r'; } else { // node value result += '"' + $(this).text() + '"'; } } else { // node name (could be parent node) // if not last, add a comma if (i > 0) result += ','; // at least one key so needs braces wrapWithBraces = true; if (lastNodeName == this.nodeName) { // more than one match - so wrap with square brackets (array) wrapWithSquareBrackets = true; } else { result += '"' + this.nodeName.toLowerCase() + '": '; lastNodeName = this.nodeName; } result = result + AttributesText(this, true); // if it contains a tag - square brackets // if it contains more than 1 tag, array, square brackets result += traverse(this, depth++); } i++; }); if (wrapWithSquareBrackets) { // put square brackets after label var insertPoint = result.indexOf(':') + 2; result = result.substr(0, insertPoint) + '[' + result.substr(insertPoint) + ']'; } if (wrapWithBraces) { result = '{' + result + '}'; } return result; } function AttributesText(elm, includeBraces) { var result = ''; if (elm.attributes.length > 0) { if (includeBraces) result += '{'; $.each(elm.attributes, function (i, attrib) { var name = attrib.name; var value = attrib.value; if (i > 0) { result += ','; } result += '"' + name + '": "' + value + '"'; }); if (includeBraces) result += '}' } return result; } function replaceAll(find, replace, str) { return str.replace(new RegExp(find, 'g'), replace); } function beautify(str) { var result = ''; var length = str.length; var i = 0; var braceCountLeft = 0; var braceCountRight = 0; var withinQuotes = false; while (i < length) { var c = str[i]; if (c == '"' && (i == 0 || c[i - 1] != '\\')) { // non-escaped quotes withinQuotes = !withinQuotes; } if (!withinQuotes && (c == '}' || c == '{' || c == ',')) { console.log('Start####' + result); // look back and remove carriage returns and whitespace that are already there var resultIndex = result.length - 1; while (resultIndex >= 0 && (result[resultIndex] == ' ' || result[resultIndex] == '\r' || result[resultIndex] == '\n' || result[resultIndex] == '\t')) { resultIndex = resultIndex - 1; result = result.substr(0, resultIndex + 1); console.log('char ' + result[resultIndex] + '-----' + result + 'zzz ' + result.length + ' ' + resultIndex); } if (c == '{') { braceCountLeft++; result += c + '\r' + GetTabs(braceCountLeft - braceCountRight); } else if (c == '}') { braceCountRight++; // precede with carriage return result += '\r' + GetTabs(braceCountLeft - braceCountRight) + c; } else if (c == ',') { result += c + '\r' + GetTabs(braceCountLeft - braceCountRight); } var nextChar = ''; // advance through whitespace and remove carriage returns that are already there while (i < length && (str[i + 1] == ' ' || str[i + 1] == '\r' || str[i + 1] == '\n' || str[i + 1] == '\t')) { i++; } } else { result += str[i]; } i++; } return result; } function GetTabs(count) { var result = ''; for (var i = 0; i < count; i++) { result += ' '; } return result; }